#pragma once
#include "Util.hpp"
#include <unordered_map>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>

typedef websocketpp::server<websocketpp::config::asio> wsserver_t;

typedef enum
{
    UNLOGIN,
    LOGIN
} ss_statu;

class session
{
public:
    session(uint64_t ssid) : _ssid(ssid)
    {
        ILOG("SESSION %p created!", this);
    }
    ~session()
    {
        ILOG("SESSION %p deleted!", this);
    }
    uint64_t ssid()
    {
        return _ssid;
    }
    void set_statu(ss_statu statu)
    {
        _statu = statu;
    }
    void set_user(uint64_t uid)
    {
        _uid = uid;
    }
    uint64_t get_user()
    {
        return _uid;
    }
    bool is_login()
    {
        return (_statu == LOGIN);
    }
    void set_timer(const wsserver_t::timer_ptr &tp)
    {
        _tp = tp;
    }
    wsserver_t::timer_ptr &get_timer()
    {
        return _tp;
    }

private:
    uint64_t _ssid;            // session标识符
    uint64_t _uid;             // session对应的用户ID
    ss_statu _statu;           // 用户状态：未登录，已登录
    wsserver_t::timer_ptr _tp; // session关联的定时器
};

#define SESSION_TIMEOUT 30000 // 单位ms,此处表示30s
#define SESSION_FOREVER -1
using session_ptr = std::shared_ptr<session>;

class session_manager
{
public:
    session_manager(wsserver_t *srv) : _next_ssid(1), _server(srv)
    {
        ILOG("session_manager init success!");
    }
    ~session_manager()
    {
        ILOG("session_manager delete success!");
    }
    session_ptr create_session(uint64_t uid, ss_statu statu)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_next_ssid));
        ssp->set_statu(statu);
        ssp->set_user(uid);
        _sessions.insert(std::make_pair(_next_ssid, ssp));
        _next_ssid++;
        return ssp;
    }
    void append_session(const session_ptr &ssp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.insert(std::make_pair(ssp->ssid(), ssp));
    }
    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _sessions.find(ssid);
        if (it == _sessions.end())
        {
            return session_ptr();
        }
        return it->second;
    }
    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.erase(ssid);
    }
    void set_session_expire_time(uint64_t ssid, int ms)
    {
        // 依赖于websocketpp的定时器来完成session生命周期的管理。
        // 登录之后，创建session，session需要在指定时间无通信后删除
        // 但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
        // 等到退出游戏大厅，或者游戏房间，这个session应该被重新设置为临时，在长时间无通信后被删除
        session_ptr ssp = get_session_by_ssid(ssid);
        if (ssp.get() == nullptr)
        {
            return;
        }
        wsserver_t::timer_ptr tp = ssp->get_timer();
        if (tp.get() == nullptr && ms == SESSION_FOREVER)
        {
            // 在session永久存在的情况下，设置永久存在
            return;
        }
        else if (tp.get() == nullptr && ms != SESSION_FOREVER)
        {
            // 在session永久存在的情况下，设置指定时间之后被删除的定时任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssid));
            ssp->set_timer(tmp_tp);
        }
        else if (tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            // 在session设置了定时删除的情况下,设置session永久存在
            //  删除定时任务--- stready_timer删除定时任务会导致任务直接被执行,session会被删除
            tp->cancel();                            // 注意这个取消定时任务并不是立即取消的(下一个执行周期),因此要用一个定时器来重新添加这个session(避免在session删除前添加)
            ssp->set_timer(wsserver_t::timer_ptr()); // 将session关联的定时器设置为空
            _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
        }
        else if (tp.get() != nullptr && ms != SESSION_FOREVER)
        {
            //  在session设置了定时删除的情况下，将session重置删除时间。
            tp->cancel();
            ssp->set_timer(wsserver_t::timer_ptr());
            _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
            // 重新给session添加定时销毁任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssp->ssid()));
            // 重新设置session关联的定时器
            ssp->set_timer(tmp_tp);
        }
    }

private:
    uint64_t _next_ssid;
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _sessions;
    wsserver_t *_server;
};